import { encode } from "punycode";

let path = require("path");
let fs = require("fs-extra");
let os = require('os');
let iconv = require('iconv-lite');

export class NodeInno {
	private spawn = require('child_process').spawn;
	private exec = 	require('child_process').exec;
	private package = require("../package.json");
	private config:any;
	private colorRed:string  = '\x1b[41m%s\x1b[0m';
	private colorGreen:string = '\x1b[32m%s\x1b[0m';
	private colorYellow:string = '\x1B[33m%s\x1B[39m';

	constructor() {
		let packPath = path.join(process.cwd(), "package.json");
		let pakage: any;
		try {
			pakage = require(packPath);
		} catch (e) {
			console.log(this.colorRed, `error: ${packPath} miss`);
		}

		if (typeof pakage["node-inno"] != "object") {
			console.log(this.colorRed, `error: "node-inno" must be an object`);
			return;
		}

		/* 编译脚本 */
		this.config = pakage["node-inno"];
		this.config.script = this.config.script || path.join(process.cwd(), "build/script/build.iss");
		
		/**
		 * 模板配置
		 */
		this.config.name = this.config.name || pakage.name;
		this.config.outFile = path.join(process.cwd(), this.config.outFile || `${pakage.name}-${pakage.version}.exe`);
	}

	/**
	 * 
	 */
	private PreprocessScript(){
		let nodeInnoBase = path.join(process.cwd(), "node-inno/src/");

		let json = fs.readJsonSync(path.join(process.cwd(), "build/build.json"), { throws: false });
		let defaultJson = fs.readJsonSync(path.join(nodeInnoBase, "template/build.json"), { throws: false });
		let defaultJsonString = fs.readFileSync(path.join(nodeInnoBase, "template/build.json"));

		if(!json){
			console.log(this.colorRed, 'cannot find file : ./build/build.json');
			return false;
		}

		let tip = "\n ↓ ↓ ↓ ↓ ↓  Please refer to the following template ↓ ↓ ↓ ↓ ↓\n";
		if(!json.app || !json.app.exe){
			console.log(this.colorRed, 'build.json => "app.exe" required');
			console.log(this.colorYellow, tip);
			console.log(this.colorYellow, defaultJsonString);
			return false;
		}

		if(!json.app || !json.app.package){
			console.log(this.colorRed, 'build.json => "app.package" required');
			console.log(this.colorYellow, tip);
			console.log(this.colorYellow, defaultJsonString);
			return false;
		}

		//let buildRoot = path.join(os.tmpdir(), "node-inno");

		let buildTempRoot = path.join(os.homedir(), "Desktop/node-inno");

		let extend = require('extend');
		extend(json, defaultJson);

		console.log(json);

		/* copy resources to temp */
		try{
			fs.ensureDirSync(buildTempRoot);
			fs.copySync(path.join(nodeInnoBase, "template/package"), buildTempRoot);
		} catch(e){
			console.error(this.colorRed, e);
			return false;
		}

		/* compile script template*/
		let template = require('art-template');
		try {
			fs.copySync(path.join(nodeInnoBase, "template/package"), buildTempRoot);

			let scripts = [
				"common.iss", 
				"config.iss",
				"const.iss",
				"installDetail.iss",
				"installFinish.iss",
				"installProgressBar.iss",
				"resetMainWindow.iss"
			];

			let renderer, tmp,  encoding = json.ui.encoding || "utf8";
			fs.ensureDirSync(path.join(buildTempRoot, "includes"));
			scripts.forEach((s, i)=>{
				tmp = fs.readFileSync(path.join(nodeInnoBase, "template/includes", s), 'utf-8');
				renderer = template.compile(tmp, {
					escape: false
				});
				fs.outputFileSync(path.join(buildTempRoot, "includes", s), iconv.encode(renderer(json), encoding));
			});

			/* copy main script to temp dir */
			fs.copySync(path.join(nodeInnoBase, "template/", "build.iss"), path.join(buildTempRoot, "build.iss"));
			return true;
		} catch (e) {
			console.error(this.colorRed, e);
			return false;
		}
	}

	/**
	 * 
	 */
	public build() {
		if(!this.PreprocessScript()){
			console.log(this.colorRed, 'node-inno config error!');
			return false;
		}

		/* inno路径 */
		let innoPath = path.join(__dirname, 'Inno_Setup_5/ISCC.exe');
		let buildProcess = this.spawn(innoPath, [this.config.script]);
		
		/* 开始编译 */
		buildProcess.stdout.on('data', (data: any) => {
			console.log(this.colorGreen, data);
		});

		buildProcess.stderr.on('data', (data: any) => {
			console.log(this.colorRed, data);
		});

		buildProcess.on('close', (code: any) => {
			console.log(this.colorGreen, `child process exited with code ${code}`);
			this.exec(this.config.outFile);
		});

		buildProcess.on('exit', (code: any) => {
			if(code === 0){
				console.log(this.colorGreen, `√ child process exited with code ${code} √`);
			} else {
				console.log(this.colorRed, `× child process exited with code ${code} ×`);
			}
		});
	}
}